package service

import (
	"context"
	"errors"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	jsoniter "github.com/json-iterator/go"
	"zhonghui/console/structs"
	"zhonghui/console/warehouse/cfgx"
	"zhonghui/console/warehouse/dbx/cache_main"
	"zhonghui/console/warehouse/dbx/db_zh_kernel"
	"zhonghui/console/warehouse/envx"
	"zhonghui/console/warehouse/helperx"
	"zhonghui/console/warehouse/logx"
	"zhonghui/console/wxutil"
)

// GenMpSchema 生成微信小程序schema码，对应短信发送记录的id
func GenMpSchema(id, path string) string {
	// return "mp_abc"
	cfg := cfgx.Cfg().GetStringMapString("mp")

	token, _ := wxutil.GetMpAccessToken(cfg["app_id"])

	req := struct {
		JumpWxa struct {
			Path  string `json:"path"`
			Query string `json:"query"`
		} `json:"jump_wxa"`
		IsExpire   bool `json:"is_expire"`
		ExpireTime int  `json:"expire_time"`
	}{}
	req.JumpWxa.Path = path
	req.JumpWxa.Query = fmt.Sprintf("record_hash=%s", id)

	j, _ := jsoniter.Marshal(req)

	res, err := helperx.SendHttpRequest(context.Background(), http.MethodPost,
		fmt.Sprintf("https://api.weixin.qq.com/wxa/generatescheme?access_token=%s", token),
		nil, j, false, 0)
	if err != nil {
		logx.Zap().Errorw("获取mp_code失败", "err", err)
		return ""
	}

	// 解析返回数据
	data := struct {
		ErrCode  int64  `json:"errcode"`
		ErrMsg   string `json:"errmsg"`
		OpenLink string `json:"openlink"`
	}{}
	if err := jsoniter.Unmarshal(res, &data); err != nil {
		logx.Zap().Errorw("解析mp_code失败", "err", err)
		return ""
	}

	// 业务失败
	if data.ErrCode != 0 {
		logx.Zap().Errorw("获取mp_code失败", "err", errors.New(data.ErrMsg))
		return ""
	}

	return data.OpenLink
}

// ToLocalDwz 生成本地的短网址url，参数是目标产品的KEY，还有record的id hash
func ToLocalDwz(product, recordHash string) string {
	return fmt.Sprintf("https://%s/dwz?t=%s&h=%s", cfgx.Cfg().GetString("console.domain"), product, recordHash)
}

// ParseLocalDwz 解析上面的短网址参数，返回拼接后的产品跳转链接，还有recordID
func ParseLocalDwz(t string) string {
	if v, ok := ProductUrl()[t]; ok {
		return v
	}
	return ""
}

// GetCorpQrcodeById 根据发送记录的id，取企微配置id和二维码
func GetCorpQrcodeById(id uint64) (string, string, error) {
	res := struct {
		ConfigId string
		QrCode   string
	}{}
	db_zh_kernel.Client().Table("sys_sms_record sr").
		Joins("join sys_task t on t.id = sr.task_id").
		Joins("join sys_contact sc on sc.config_id = t.config_id").
		Where("sr.id = ?", id).
		Select("t.config_id, sc.qr_code").
		Scan(&res)

	if res.ConfigId == "" || res.QrCode == "" {
		return "", "", errors.New("invalid id")
	}

	return res.ConfigId, res.QrCode, nil
}

// GenBaiduDwzBatch 生成百度短网址，100个一批
func GenBaiduDwzBatch(l []string) []structs.DwzRespBody {
	resp := make([]structs.DwzRespBody, 0)

	list := make([]string, 0)

	// 不需要生成短网址的，从list里面移除
	for _, d := range l {
		if strings.HasPrefix(d, "NONE") {
			resp = append(resp, structs.DwzRespBody{
				Code:     0,
				LongUrl:  strings.TrimPrefix(d, "NONE_"),
				ShortUrl: "NONE",
				ErrMsg:   "",
			})
		} else {
			list = append(list, d)
		}
	}

	for len(list) > 0 {
		var l []string
		if len(list) > 5 {
			l = list[:5]
			list = list[5:]
		} else {
			l = list
			list = nil
		}

		time.Sleep(time.Millisecond * 50)
		res := genBaiduDwz(l)
		for _, r := range res {
			if r.Code != 0 {
				continue
			}
			resp = append(resp, r)
		}
	}

	return resp
}

func GenZhDwzBatch(l []string) []structs.DwzRespBody {
	resp := make([]structs.DwzRespBody, 0)

	for _, item := range l {
		// 解析 URL
		parsedURL, err := url.Parse(item)
		if err != nil {
			log.Fatal(err)
		}

		// 提取查询参数
		queryParams := parsedURL.Query()
		hValue := queryParams.Get("h")

		resp = append(resp, structs.DwzRespBody{
			Code:     0,
			LongUrl:  item,
			ShortUrl: fmt.Sprintf("https://j.pubmi.cn/d/%s", hValue),
			ErrMsg:   "",
		})
	}

	return resp
}

// GenBaiduDwz 生成百度短网址
func genBaiduDwz(list []string) []structs.DwzRespBody {
	if !envx.InProd() {
		ts := make([]structs.DwzRespBody, 0)
		for _, i := range list {
			ts = append(ts, structs.DwzRespBody{
				Code:     0,
				LongUrl:  i,
				ShortUrl: "https://dwz.cn/fake-" + (uuid.New().String())[:8],
			})
		}
		return ts
	}

	req := make([]structs.DwzReq, 0)
	for _, s := range list {
		req = append(req, structs.DwzReq{
			LongUrl:        s,
			TermOfValidity: "1-year",
		})
	}

	j, _ := jsoniter.Marshal(req)
	headers := map[string]string{}
	headers["Dwz-Token"] = cfgx.Cfg().GetString("dwz.baidu.token")

	res, err := helperx.SendHttpRequest(context.Background(), http.MethodPost,
		fmt.Sprintf("https://dwz.cn/api/v3/short-urls"),
		headers, j, false, 30*time.Second)
	if err != nil {
		logx.Zap().Errorw("获取dwz失败", "err", err)
		return nil
	}

	// 解析返回数据
	data := structs.DwzResp{ShortUrls: make([]structs.DwzRespBody, 0)}
	if err := jsoniter.Unmarshal(res, &data); err != nil {
		logx.Zap().Errorw("解析dwz失败", "err", err)
		return nil
	}

	// 业务失败
	if data.Code != 0 {
		logx.Zap().Errorw("获取dwz失败", "err", errors.New(data.ErrMsg))
		return nil
	}

	return data.ShortUrls
}

// MpCode2User 小程序code换取用户信息
func MpCode2User(c context.Context, code string) (string, string) {
	if code == "" {
		return "", ""
	}

	u := fmt.Sprintf("https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code",
		cfgx.Cfg().GetString("mp.app_id"),
		cfgx.Cfg().GetString("mp.secret"),
		code,
	)
	res, err := helperx.SendHttpRequest(c, http.MethodGet, u, nil, nil, false, 0)
	if err != nil {
		logx.ZapTrace(c).Errorw("获取小程序用户信息失败", "err", err)
		return "", ""
	}

	resp := struct {
		OpenId     string `json:"openid"`
		SessionKey string `json:"session_key"`
		UnionId    string `json:"unionid"`
		ErrCode    int64  `json:"errcode"`
		ErrMsg     string `json:"errmsg"`
	}{}
	_ = jsoniter.Unmarshal(res, &resp)

	if resp.ErrCode != 0 && resp.ErrMsg != "" {
		logx.ZapTrace(c).Errorw("获取小程序用户信息失败", "err", errors.New(resp.ErrMsg))
		return "", ""
	}

	return resp.UnionId, resp.OpenId
}

// HandleSensitiveKey 访问的时候埋一个点，记录用户设备指纹，更换设备后下一步跳转，就不会传递sensitive_key了
func HandleSensitiveKey(c *gin.Context, phone, recordHash string) string {
	key := fmt.Sprintf("record_sk2_%s", recordHash)

	sensitiveKey := helperx.EasySHA1(fmt.Sprintf("%s%s", phone, cfgx.Cfg().GetString("console.zh_key")))
	return sensitiveKey

	// 先取redis
	rk := cache_main.Client().Get(c, key).Val()
	// 再取cookie
	ck, _ := c.Cookie(key)

	// redis没有，表示是第一次访问，直接返回
	if rk == "" {
		cache_main.Client().Set(c, key, helperx.EasyMD5(sensitiveKey), 2592000*time.Second)
		c.SetCookie(key, helperx.EasyMD5(sensitiveKey), 2592000, "/", c.Request.Host, false, true)
		return sensitiveKey
	}

	// cookie不匹配，第一次不是当前设备访问的，返回空
	if ck != rk {
		return ""
	}

	// 上次访问跟本次是相同设备
	return sensitiveKey
}
